<!-- 数据库管理界面，虚拟滚动树 -->
<template>
  <lay-card>
    <lay-row space="10">
      <lay-col>
        <lay-input
          v-model="queryName"
          :allow-clear="true"
          @clear="clearAndGetLinks"
          size="sm"
          placeholder="请输入查询，区分大小写"
          @change="filterDbTrees"
          >
            <template #suffix>
              <lay-icon type="layui-icon-search" @click="filterDbTrees" size="sm"></lay-icon>
            </template>
          </lay-input>
      </lay-col>
    </lay-row>
    <lay-row space="10">
      <lay-col>
<!--        <lay-button size="xs" @click="expandAllEvent">全部展开</lay-button>-->
        <lay-button size="xs" @click="closeExpandEvent">收起所有</lay-button>
        <lay-button size="xs" @click="submitChecked" v-if="showCheckbox">选定</lay-button>
        <lay-button size="xs" @click="closeLayUI" v-if="showCheckbox">关闭</lay-button>
      </lay-col>
    </lay-row>
    <lay-row space="10">
      <lay-col>
          <vxe-table
            show-overflow
            :height="scrollHeight"
            ref="tableRef"
            border="outer"
            :round="true"
            :loading="loading"
            :show-header="false"
            :tree-config="treeConfig"
            :row-config="{isCurrent: true, height: 35, isHover: true, keyField: 'id'}"
            :checkbox-config="checkBoxConfig"
            :scroll-y="{enabled: true, gt: 20}"
            :data="tableData"
          >
            <vxe-column
              field="name"
              title="名称"
              sortable
              :type="showCheckbox === true ? 'checkbox':'seq'"
              :width="showCheckbox === true ? '70%':'90%'"
              tree-node
              isCurrent
            >
              <template #default="{ row }">
                <a
                  type="text"
                  :class="!showCheckbox && row.type === 'db_table' ? 'CheckRow' : 'NoCheckRow'"
                  @click="viewDetail(row)"
                  style="width: 100%; overflow: hidden;"
                >
                  <lay-icon :type="setIconButton(row)"></lay-icon>{{ row.name }}
                </a>
              </template>
            </vxe-column>
            <vxe-column
                type="seq"
                width="10%"
            >
              <template #default="{ row }">
                <div v-if="!showCheckbox && (row.type === 'db_root' || row.type === 'db_type' | row.type === 'db_link') ">
                  <lay-icon
                    type="layui-icon-addition"
                    v-if="row.type === 'db_root'"
                    @click="addDBType(row)"
                    style="margin-left: 10px"
                  ></lay-icon>
                  <lay-dropdown trigger="hover" updateAtScroll>
                  <lay-icon
                    type="layui-icon-more"
                    v-if="row.type === 'db_type' || row.type === 'db_link'"
                    style="margin-left: 5px"
                  ></lay-icon>
                  <template #content>
                    <lay-card>
                      <lay-row space="10" v-if="row.type == 'db_type'">
                        <lay-col md="22">
                          <lay-dropdown-menu-item @click="addDBSource(row, 'add')">
                            <lay-icon type="layui-icon-add-circle"></lay-icon>新增
                          </lay-dropdown-menu-item>
                        </lay-col>
                      </lay-row>
                      <lay-row space="10" v-if="row.type == 'db_link'">
                        <lay-col md="22">
                          <lay-dropdown-menu-item @click="addDBSource(row, 'update')">
                            <lay-icon type="layui-icon-edit"></lay-icon>编辑
                          </lay-dropdown-menu-item>
                        </lay-col>
                      </lay-row>
                      <lay-row space="10">
                        <lay-col md="22">
                          <lay-dropdown-menu-item @click="deleteNode(row)">
                            <lay-icon type="layui-icon-delete"></lay-icon>删除
                          </lay-dropdown-menu-item>
                        </lay-col>
                      </lay-row>
                    </lay-card>
                  </template>
                </lay-dropdown>
                </div>
              </template>
            </vxe-column>
          </vxe-table>
      </lay-col>
    </lay-row>
  </lay-card>
</template>

<script lang="ts">

export default {
    name: "VirtualTree",
}
</script>

<script setup lang="ts">
import {onMounted, ref, getCurrentInstance, reactive, h} from 'vue';
import {VxeTableInstance, VxeTablePropTypes} from "vxe-table";
import {layer} from "@layui/layer-vue";
import {
  deleteLink,
  getAllLinks,
  queryAllBases,
  queryAllColumnTree,
  queryAllTables
} from "../../api/module/db";
import {notify} from "../../untils/tip";
import AddDataSource from "./AddDataSource.vue";
import AddSourceType from "./AddSourceType.vue";
import {RowVO} from '../../store/interface/VirtulTreeInterface';
import {TreeNews} from "../../store/databases/TreeNews";
import {Source} from "../../store/interface/VirtulTreeInterface";

const treeService = new TreeNews();
const tableRef = ref<VxeTableInstance<RowVO>>();
const loading = ref(false);
const tableData = ref<RowVO[]>([]);

// 组件入参
let {scrollHeight, showCheckbox, target, checkKeys} = defineProps({
    scrollHeight: {type: String, default: '700'},
    target: {type: String, default: ref('right')},
    showCheckbox: {type: Boolean, default: ref(false)},
    checkKeys: {type: Array}
});

/** 定义一个变量来接收父组件传来的方法 **/
const emit = defineEmits(["insertTables", "cancelCheck"]);

// 页面加载前执行的初始方法
let instance = getCurrentInstance();

onMounted(() => {
  loadList();
});

// 取消
const closeLayUI = () => {
  emit('cancelCheck');
}

// 虚拟滚动树的配置项
const treeConfig = reactive<VxeTablePropTypes.TreeConfig<RowVO>>({
  lazy: true,       // 开启懒加载
  transform: true,  // 开启树形结构
  indent: 15,       // 树节点的缩进
  rowField: 'id',
  accordion: false,  // 对于同一级的节点，每次只能展开一个
  reserve: true,     // 是否保留展开状态
  parentField: 'parentId',
  hasChildField: 'hasChild',
  expandRowKeys: ['db_root.1'],    // 展开的节点
  loadMethod ({ row }) {
    // 异步加载子节点
    return new Promise(resolve => {
      const childNodes = viewDetail(row);
      resolve(childNodes)
    })
  }
});

// 复选框的配置项
const checkBoxConfig = reactive<VxeTablePropTypes.CheckboxConfig<RowVO>>({
  checkStrictly: false,
  checkField: 'checked',
  highlight: showCheckbox === true,
  labelField: 'name',
  checkRowKeys: checkKeys
})

// old params
const queryName = ref('');  // 查询节点名称

// 数据库目录树展示的节点类型的值，定义如下
const loadList = async () => {
  loading.value = true
  tableData.value = [];
  let {result, status, message} = await getAllLinks();
  if (status === 'success'){
      tableData.value = result;
      treeService.clearTree();
      treeService.addNodes(result);
  }else {
      tableData.value = [
          {id: '0', hasChild: false, parentId: null, name: "数据源", type: "db_root",
              checked: false, dbLink: null, dbBase: null, dbTable: null,
              dbField: null, data: null, children: []},
      ];
      notify("failed", message);
  }
  loading.value = false;
};

// 节点树的设置和操作
const setIconButton = (row: RowVO): string => {
  const iconText = ref('vxe-icon-send');
  switch (row.type) {
    case 'db_root' : iconText.value = 'layui-icon-home'; break;
    case 'db_type' : iconText.value = 'layui-icon-chart'; break;
    case 'db_link' : iconText.value = 'layui-icon-unlink'; break;
    case 'db_base' : iconText.value = 'layui-icon-layer'; break;
    case 'db_table' : iconText.value = 'layui-icon-table'; break;
    // case 'db_field' : iconText.value = 'vxe-icon-star'; break;
  }
  return iconText.value;
};

// 点击节点，查看详情
const viewDetail = async (row: RowVO) => {
  const $table = tableRef.value;
  const childNodes = ref<RowVO[]>([]);
  switch (row.type) {
    case 'db_root' :
      $table?.toggleTreeExpand(row);
      break;
    case 'db_type' :
      $table?.toggleTreeExpand(row);
      break;
    case 'db_link' :
      $table?.toggleTreeExpand(row);
      let baseResult = await queryAllBases({id: row.id});
      if (baseResult.status === 'success') {
        treeService.addNodes(baseResult.result);
        childNodes.value = baseResult.result;
      } else {
        notify(baseResult.status, baseResult.message)
      }
      break;
    case 'db_base' :
      $table?.toggleTreeExpand(row);
      let tableResult = await queryAllTables({db_id: row.dbLink, database: row.dbBase});
      if (tableResult.status === 'success') {
        if (!showCheckbox) {
          tableResult.result.forEach((unit: { hasChild: boolean; }) => {
            unit.hasChild = false
          })
        }
        treeService.addNodes(tableResult.result);
        childNodes.value = tableResult.result;
      } else {
        notify(tableResult.status, tableResult.message);
      }
      break;
    case 'db_table':
      if (showCheckbox) {
        $table?.toggleTreeExpand(row);
        const params = reactive({db_id: row.dbLink, database: row.dbBase, table: row.dbTable});
        console.log("showCheckbox: ", showCheckbox, "params: ", params)
        let columnResult = await queryAllColumnTree(params);
        if (columnResult.status === 'success') {
          treeService.addNodes(columnResult.result);
          childNodes.value = columnResult.result;
        } else {
          notify(columnResult.status, columnResult.message);
        }
      } else {
        console.log('查看表的字段信息或表数据');
        instance?.proxy?.$Bus.emit('on-table', row);
      }
      break;
    case 'db_field' :
      // layer.msg("待补充，查询表下面的字段信息");
      break;
  }
  // 异步加载子节点
  return childNodes.value;
};

const addDBSource = (node: any, operate: string = 'add') => {
  console.log('addDBSource:', node)
  // const db_link = reactive({flag: '', parent_id: '', id: '', name: '', type: node.name, db_type: '', children: []})
  const title_name = ref('');
  // 数据库连接信息
  const db_link = ref<Source>({
    name: '',
    dataBaseType: node.name,
    ip: '',
    port: 0,
    serverName: '',
    userName: '',
    passWord: '',
    remark: ''
  })
  const currentNode = ref({id: null, hasChild: true, parentId: node.id, name: "", type: "db_link",
            checked: false, dbLink: null, dbBase: null, dbTable: null,
            dbField: null, data: db_link, children: []})
  if (operate === 'add') {
    title_name.value = "新增数据库连接";
  }else {
    currentNode.value = node;
    title_name.value = "更新数据库连接";
  }
  console.log(db_link)
  db_lay_id.value = layer.open({
    type: 'page',
    title: title_name.value,
    area: ['400px','650px'],
    content: h(AddDataSource, {
      operate: operate,
      currentNode: currentNode.value,
      updateData: function (node_dict: any) {
        console.log('addDBSource updateData:', node_dict)
        if(node_dict.flag === 'add'){
          tableData.value.push(node_dict);
          treeService.addNode(node_dict);
        }else if(node_dict.flag === 'update'){
          node.name = node_dict.name;
          node.data = node_dict.data;
          treeService.updateNodeById(node_dict);
        }else {
          layer.msg('不确定的操作类型！')
        }
        loadList();
        layer.close(db_lay_id.value);
      },
      closeLayUi: function (){
        layer.close(db_lay_id.value);
      }
    }),
  })
};

const deleteNodeOperate = async (node: any) => {
  let {result, status, message} = await deleteLink({id: node.id, type: node.type});
  if(status === 'success'){
    await loadList();
  }else {
    layer.msg(message);
  }
};

const deleteNode = (node: any) => {
  console.log('deleteNode', node)
  layer.confirm('确认要删除该节点么？', {
    btn: [
        {text:'确认', callback: (id: any) => {
            deleteNodeOperate(node);
            layer.close(id); }
        },
        {text:'取消', callback: (id: any) => {
          layer.close(id);}
        }
    ]
  })
};

const addDBType = (node: any) => {
  const operate = 'add';
  const currentNode = reactive({id: null, hasChild: true, parentId: node.id, name: "", type: "db_type",
              checked: false, dbLink: null, dbBase: null, dbTable: null,
              dbField: null, data: null, children: []})
  db_lay_id.value = layer.open({
    title: "新增数据库类型",
    area: ['350px','120px'],
    content: h(AddSourceType, {
      operate: operate,
      childNode: currentNode,
      updateRootNode: function (ch_node: any) {
        if (ch_node.id !== '') {
          tableData.value.push(ch_node);
          treeService.addNode(ch_node);
        }else {
          layer.msg('添加节点失败，节点id为空！')
        }
        loadList();
        layer.close(db_lay_id.value);
      }
    }),
  })
};

// 记录弹框ID，用于关闭弹框
const db_lay_id = ref();


// 全部展开所有节点
const expandAllEvent = () => {
  const $table = tableRef.value
  if ($table) {
    $table.setAllTreeExpand(true)
  }
};

// 全部展开所有节点
const closeExpandEvent = () => {
  const $table = tableRef.value
  if ($table) {
    $table.clearTreeExpand()
  }
};

// 选定
const submitChecked = () => {
  const $table = tableRef.value;
  if ($table) {
    const checkRecord = $table.getCheckboxRecords();
    const targetTables = treeService.filterTableParams(checkRecord);
    console.log('submitChecked:', checkRecord, 'targetTables:', targetTables, 'target:', target)
    emit('insertTables', targetTables, target);
  }
};

// 清楚查询信息
const clearAndGetLinks = () => {
    tableData.value = treeService.getTree();
};


// 查询树，筛选树
const filterTargetTrees = (rawTreeData: any) => {
    loading.value = true;
    console.log('filterTargetTrees: ', rawTreeData )
    treeService.clearResultTree();
    treeService.filterTree(rawTreeData, (node: RowVO)=>{
        const regex_pattern = new RegExp(queryName.value, "i");
        return node.name?.match(regex_pattern);
    })
    const newTrees = treeService.getResultTrees();
    console.log('newTrees:', newTrees)
    if(newTrees){
       tableData.value = newTrees;
       notify("success", "查找成功，刷新页面！")
    }else {
        notify("failed", "查找失败，不存在！")
    }
    loading.value = false;
};

// 查询 逻辑如下：
const filterDbTrees = () => {
  if(queryName.value.length > 0){
    const $table = tableRef.value;
    const currentData = $table?.data;
    console.log('currentData:', currentData)
    if(currentData && currentData?.length > 0){
        filterTargetTrees(currentData[0]);
    }
  }else {
    clearAndGetLinks();
  }
};
</script>

<style scoped>
.CheckRow {
  color: blue;
  text-decoration: underline;
}

.NoCheckRow {
  color: black;
  /*text-decoration: underline; */
}
</style>